import _ from 'lodash';
import downloadjs from 'downloadjs';
import router from '@/router/index.ts';

const TOKEN_KEY = 'apiauthtoken';
const API_BASE = '/i';

export function getJWT() {
  return localStorage.getItem(TOKEN_KEY);
}

export function setJWT(jwt) {
  if (jwt === null) return localStorage.removeItem(TOKEN_KEY);
  else return localStorage.setItem(TOKEN_KEY, jwt);
}

function gotoLogin() {
  if (router.currentRoute.value?.path !== '/login') {
    setJWT(null);
    router.replace('/login');
  }
}

function blobFilename(v = '') {
  const filename =
    v.match(/filename=?"?([^"=]+)/)?.[1] || v.match(/[\w\.%-]+$/)?.[0] || '';
  return decodeURIComponent(filename) || '下载文件';
}

export async function theFetch(url, opts = {}) {
  url = url.trim();
  if (!url.startsWith('/')) url = '/' + url;
  if (!getJWT()) {
    const path = new URL(location.origin + url).pathname;
    const whiteList = ['/captcha', '/login'];
    if (!whiteList.includes(path)) {
      gotoLogin();
      return Promise.reject('Authorization required');
    }
  }
  url = API_BASE + url;
  const defaultOpts = {
    credentials: 'omit',
    headers: {},
  };
  if (getJWT()) defaultOpts.headers.Authorization = getJWT();
  if (opts.isRobot) defaultOpts.headers['X-ROBOT'] = 1;
  const resp = await fetch(url, _.defaultsDeep(opts, defaultOpts));
  if (opts.asReader) {
    const reader = resp.body.getReader();
    return { reader, resp };
  }
  if (!resp.ok) {
    const isJSON = resp.headers.get('Content-Type')?.includes('json');
    const data = isJSON ? await resp.json() : await resp.text();
    return Promise.reject({ resp, data });
  }
  if (opts.isBlob) {
    const bin = await resp.blob();
    return { bin, resp };
  } else if (opts.isText) {
    const text = await resp.text();
    return { text, resp };
  } else {
    const data = await resp.json();
    if (data.code !== 200) {
      if (data.code === 401) {
        gotoLogin();
      }
      return Promise.reject(data);
    }
    return { resp, ...data };
  }
}

function urlWithQuery(url, data = {}) {
  let args = new URLSearchParams(data).toString();
  if (args) url += '?' + args;
  return url;
}

function setHeader(k, v, opts = {}) {
  opts.headers = opts.headers || {};
  opts.headers[k] = v;
}

function setContentType(type, opts = {}, data = {}) {
  if (type === 'json') {
    setHeader('Content-Type', 'application/json', opts);
    opts.body = JSON.stringify(data);
  } else if (type === 'form') {
    setHeader('Content-Type', 'application/x-www-form-urlencoded', opts);
    opts.body = new URLSearchParams(data).toString();
  }
}

export const api = {
  get(url, data = {}, opts = {}) {
    return theFetch(urlWithQuery(url, data), opts);
  },
  post(url, data = {}, opts = {}) {
    opts.method = 'POST';
    setContentType('json', opts, data);
    return theFetch(url, opts);
  },
  put(url, data = {}, opts = {}) {
    opts.method = 'PUT';
    setContentType('json', opts, data);
    return theFetch(url, opts);
  },
  delete(url, data = {}, opts = {}) {
    opts.method = 'DELETE';
    return theFetch(urlWithQuery(url, data), opts);
  },
  form(url, data = {}, opts = {}) {
    opts.method = 'POST';
    setContentType('form', opts, data);
    return theFetch(url, opts);
  },
  upload(url, data = {}, opts = {}) {
    opts.method = 'POST';
    opts.body = new FormData();
    for (let k in data) {
      opts.body.append(k, data[k]);
    }
    return theFetch(url, opts);
  },
  blob(url, data = {}, opts = {}) {
    opts.isBlob = true;
    opts.method = opts.method || 'GET';
    if (opts.method === 'GET') {
      url = urlWithQuery(url, data);
    } else {
      const contentType = opts.contentType || 'json';
      setContentType(contentType, opts, data);
    }
    return theFetch(url, opts);
  },
  async download(url, data = {}, opts = {}) {
    const { bin, resp } = await this.blob(url, data, opts);
    const filename =
      opts.saveName || blobFilename(resp.headers.get('Content-Disposition'));
    downloadjs(bin, filename);
    return { filename, bin, resp };
  },
};

export async function blobUrl(bucketName, fileName, opts = {}) {
  const { bin, resp } = await api.blob(
    'general/download',
    {
      bucketName,
      fileName,
    },
    opts,
  );
  const url = URL.createObjectURL(bin);
  return { url, bin, resp };
}

export function blobDownload(bucketName, fileName, saveName = null) {
  return api.download(
    'general/download',
    { bucketName, fileName },
    { saveName },
  );
}

export function fetchCaptcha() {
  return api.get('captcha', { v: new Date().getTime() }, { isBlob: true });
}

export async function cmd(clientId, type, dir, cmd, args = '') {
  const { data } = await api.post('general/commitCmd', {
    clientId,
    type,
    dir,
    cmd,
    args,
  });
  return data;
}

export function fileUpload(file, bucketName = 'common') {
  return api.upload('general/upload', { file, bucketName });
}

export function readStream(reader, write, flush) {
  reader.read().then(({ done, value }) => {
    if (done) {
      flush();
    } else {
      write(value);
      readStream(reader, write, flush);
    }
  });
}
